
;window.ServiceFactory = (function () {
  var
    isInit = false,
    defaultAvatar = '',
    defaultAvatarMedium = '',
    defaultAvatarSmall = '',
    formatDatetime = null,
    formatDate = null,
    formatTime = null,
    monthNames = null,
    myProfile = null,
    portalSettings = {},
    apiAnchors = [
      {handler : 'authentication', re : /authentication\.json/},
      {handler : 'settings', re : /settings\.json/},
      {handler : 'isme', re : /people\/@self\.json/},

      {handler : 'cmt-blog', re : /blog\/[\w\d-]+\.json/},

      {handler : 'prj-task', re : /project\/task\/[\w\d-]+\.json/},
      {handler : 'prj-tasks', re : /project\/[\w\d]+\/task\.json/},
      {handler : 'prj-comments', re : /project\/message\/[\w\d-]+\/comment\.json/},
      {handler : 'prj-comments', re : /project\/comment\/[\w\d-]+\.json/},
      {handler : 'prj-discussion', re : /project\/message\/[\w\d-]+\.json/},
      {handler : 'prj-discussions', re : /project\/[\w\d-]+\/message\.json/},
      {handler : 'prj-projects', re : /project[\/]*[@self|@follow]*\.json/}
    ];

  function isArray (o) {
   return o.constructor.toString().indexOf("Array") != -1;
  }

  function extend (src, dsc) {
    for (var fld in dsc) {
      if (dsc.hasOwnProperty(fld)) {
        src[fld] = dsc[fld];
      }
    }
    return src;
  };

  function collection (items, hCreate, hExtend) {
    var
      collection = [],
      itemsInd = items ? items.length : 0;

    while (itemsInd--) {
      collection.unshift(extend(hCreate(items[itemsInd]), hExtend(items[itemsInd])));
    }

    return collection;
  }

  function leftPad (n, m) {
    var p = '000000000';
    n = '' + n;
    if (!m) {
      return n.length === 1 ? '0' + n : n;
    }
    return n.length === m ? n : p.substring(0, m - n.length) + n;
  }

  var init = function (opts) {
    if (isInit === true) {
      return undefined;
    }
    isInit = true;

    opts = opts || {};

    if (opts.hasOwnProperty('monthNames')) {
      monthNames = opts.monthNames;
    }

    if (opts.hasOwnProperty('avatars')) {
      defaultAvatar = opts.avatars.hasOwnProperty('defaultAvatar') ? opts.avatars.defaultAvatar : defaultAvatar;
      defaultAvatarMedium = opts.avatars.hasOwnProperty('defaultAvatarMedium') ? opts.avatars.defaultAvatarMedium : defaultAvatarMedium;
      defaultAvatarSmall = opts.avatars.hasOwnProperty('defaultAvatarSmall') ? opts.avatars.defaultAvatarSmall : defaultAvatarSmall;
    }

    if (opts.hasOwnProperty('formats')) {
      formatDatetime = opts.formats.hasOwnProperty('formatDatetime') ? opts.formats.formatDatetime : formatDatetime;
      formatDate = opts.formats.hasOwnProperty('formatDate') ? opts.formats.formatDate : formatDate;
      formatTime = opts.formats.hasOwnProperty('formatTime') ? opts.formats.formatTime : formatTime;
    }
  };

  var serializeDate = function (d) {
    var date = null, timestamp = d && typeof d === 'object' && d.hasOwnProperty('utc') && d.utc || d;
    var offset = d && typeof d === 'object' && d.hasOwnProperty('offset') && isFinite(+d.offset) && +d.offset || 0;
    if (typeof timestamp !== 'string') {
        return null;
    }

    //date = new Date(Date.parse(timestamp));
    //date = date instanceof Date ? date : null;

    if (date === null) {
      offset = 0;
      if (timestamp.indexOf('Z') === -1) {
        offset = timestamp.substring(timestamp.length - 5).split(':');
        offset = (+offset[0] * 60 + +offset[1]) * (timestamp.charAt(timestamp.length - 6, 1) === '+' ? 1 : -1);
      }
      date = timestamp.split('.')[0].split('T');
      date[0] = date[0].split('-');
      date[1] = date[1].split(':');

      date = new Date(
        Date.UTC(
          +date[0][0],
          +date[0][1] - 1,
          +date[0][2],
          +date[1][0],
          +date[1][1] - offset * 2,
          +date[1][2],
          0
        )
      );
    }
    date = date instanceof Date ? date : null;

    return date = date instanceof Date ? date : new Date(0);
  };

  var serializeTimestamp = function (d) {
    return d instanceof Date ? '' + [d.getFullYear(), leftPad((d.getMonth() + 1)), leftPad(d.getDate())].join('-') + 'T' + [leftPad(d.getHours()), leftPad(d.getMinutes()), leftPad(d.getSeconds())].join(':') + '.' + leftPad(d.getMinutes(), 7) + 'Z' : '';
    return JSON.stringify(d);
  };

  var formattingDate = function (d, fmt, monthnames) {
    var c = '', r = [], escape = false, hours = d.getHours(), isam = hours < 12;

    monthnames = monthnames || ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

    if (fmt.search(/%p|%P/) !== -1) {
      if (hours > 12) {
        hours = hours - 12;
      } else if (hours == 0) {
        hours = 12;
      }
    }
    for (var i = 0, n = fmt.length; i < n; ++i) {
      c = fmt.charAt(i);
      if (escape) {
        switch (c) {
          case 'h' : c = '' + hours; break;
          case 'H' : c = leftPad(hours); break;
          case 'M' : c = leftPad(d.getMinutes()); break;
          case 'S' : c = leftPad(d.getSeconds()); break;
          case 'd' : c = '' + d.getDate(); break;
          case 'm' : c = '' + (d.getMonth() + 1); break;
          case 'y' : c = '' + d.getFullYear(); break;
          case 'b' : c = '' + monthnames[d.getMonth()]; break;
          case 'p' : c = (isam) ? ('' + 'am') : ('' + 'pm'); break;
          case 'P' : c = (isam) ? ('' + 'AM') : ('' + 'PM'); break;
        }
        r.push(c);
        escape = false;
      } else {
        if (c == '%') {
          escape = true;
        } else {
          r.push(c);
        }
      }
    }
    return r.join('');
  }

  var getDisplayDatetime = function (date) {
    var displaydate = date ? date.toLocaleDateString() : '';
    if (date && formatDatetime) {
      displaydate = formattingDate(date, formatDatetime, monthNames);
    }
    return displaydate;
  };

  var getGroups = function (o) {
    return [];
  };

  var getContacts = function (o) {
    return {};
  };

  var createPerson = function (o) {
    if (!o) {
      return null;
    }
    if (o.hasOwnProperty('Email') && o.hasOwnProperty('Contacts')) {
      o.Contacts = [{ Type : 'mail', Value : o.Email}].concat(o.Contacts);
    }

    var
      person = null,
      crtdate = serializeDate(o.Created || o.WorkFromDate),
      trtdate = serializeDate(o.Terminated || o.TerminatedDate),
      displayname = o.DisplayName || o.FirstName + ' ' + o.LastName,
      contacts = getContacts(o.Contacts);

    person = {
      index : displayname.charAt(0).toLowerCase(),
      type : 'person',
      id : o.Id,
      timestamp : crtdate ? crtdate.getTime() : 0,
      crtdate : crtdate,
      displaycrtdate : getDisplayDatetime(crtdate),
      trtdate : trtdate,
      displaytrtdate : getDisplayDatetime(trtdate),
      username : o.UserName || '',
      firstname : o.FirstName || '',
      lastname : o.LastName || '',
      displayname : displayname || '',
      email : o.Email || '',
      //tel : contacts.telephones.length > 0 ? contacts.telephones[0].val : '',
      //contacts : contacts,
      avatar : o.Avatar || o.AvatarSmall || defaultAvatar,
      avatarsmall : o.AvatarSmall || defaultAvatarSmall,
      groups : getGroups(o.Groups),
      isme : myProfile ? myProfile.id === o.Id : false,
      ismanager : false,
      status : o.Status || 0,
      sex : o.Sex || '',
      location : o.Location || '',
      title : o.Title || '',
      notes : o.Notes || ''
    };

    return person;
  };

  var factories = {
    authentication : function (response) {
      return {
        token : response.Token
      };
    },

    isme : function (response) {
      myProfile = createPerson(response);
      myProfile.isme = true;

      return myProfile;
    },

    settings : function (response) {
      portalSettings = {
        type : 'settings',
        culture : response.Culture,
        timezone : response.Timezone,
        trusteddomains : response.TrustedDomains,
        trustedDomainstype : response.TrustedDomainsType,
        utchoursoffset : response.UtcHoursOffset,
        utcoffset : response.UtcOffset
      };

      return portalSettings;
    }
  };

  /* community */
  factories.cmt = {
    item : function (response) {
      var
        crtdate = serializeDate(response.Created),
        uptdate = serializeDate(response.Updated);

      return {
        id : response.Id,
        timestamp : crtdate ? crtdate.getTime() : 0,
        crtdate : crtdate,
        displaycrtdate : getDisplayDatetime(crtdate),
        uptdate : uptdate,
        displayuptdate : getDisplayDatetime(uptdate),
        createdby : createPerson(response.CreatedBy || response.Author),
        updatedby : createPerson(response.UpdatedBy || response.CreatedBy || response.Author)
      };
    },

    blog : function (response) {
      return extend(this.item(response), {
        type : 'blog'
        //blah-blah-blah
      });
    },

    poll : function (response) {
      return extend(this.item(response), {
        type : 'poll'
        //blah-blah-blah
      });
    },

    forum : function (response) {
      return extend(this.item(response), {
        type : 'forum'
        //blah-blah-blah
      });
    },

    event : function (response) {
      var
        item = this.item(response);

      return extend(item, {
        type : 'event'
        //blah-blah-blah
      });
    },

    bookmark : function (response) {
      return extend(this.item(response), {
        type : 'bookmark'
        //blah-blah-blah
      });
    }
  };

  /* projects */
  factories.prj = {
    item : function (response) {
      var
        crtdate = serializeDate(response.Created),
        uptdate = serializeDate(response.Updated);

      return {
        id : response.Id,
        timestamp : crtdate ? crtdate.getTime() : 0,
        crtdate : crtdate,
        displaycrtdate : getDisplayDatetime(crtdate),
        uptdate : uptdate,
        displayuptdate : getDisplayDatetime(uptdate),
        createdby : createPerson(response.CreatedBy || response.Author),
        updatedby : createPerson(response.UpdatedBy),
        responsible : createPerson(response.Responsible),
        //canedit : response.CanEdit || false,
        canedit : response.CanEdit || true,
        title : response.Title || '',
        description : response.Description || ''
      };
    },

    task : function (response) {
      return extend(this.item(response), {
        type : 'task',
        projectid : response.ProjectOwner.Id,
        projecttitle : response.ProjectOwner.Title,
        status : response.Status,
        priority : response.Priority,
        isexpired : response.IsExpired
      });
    },

    tasks : function (response) {
      return collection(response, this.item, function (response) {
        return {
          type : 'task',
          projectid : response.ProjectOwner.Id,
          projecttitle : response.ProjectOwner.Title,
          milestoneid : response.MilestoneId,
          status : response.Status
        };
      });
    },

    discussion : function (response, responses) {
      var comments = [];
      if (responses.length === 2) {
        comments = this.comments(responses[1]);
      }

      return extend(this.item(response), {
        type : 'discussion',
        projectid : response.ProjectOwner.Id,
        projecttitle : response.ProjectOwner.Title,
        parentid : null,
        comments : comments,
        text : response.Text || ''
      });
    },

    comments : function (response) {
      if (!isArray(response)) {
        return extend(this.item(response), {
          type : 'comment',
          parentid : response.ParentId === '00000000-0000-0000-0000-000000000000' ? null : response.ParentId,
          comments : [],
          text : response.Text || ''
        });
      }
      return collection(response, this.item, function (response) {
        return {
          type : 'comment',
          parentid : response.ParentId === '00000000-0000-0000-0000-000000000000' ? null : response.ParentId,
          comments : [],
          text : response.Text || ''
        };
      });
    },

    discussions : function (response) {
      return collection(response, this.item, function (response) {
        return {
          type : 'discussion',
          projectid : response.ProjectOwner.Id,
          projecttitle : response.ProjectOwner.Title,
          text : response.Text || ''
        };
      });
    },

    projects : function (response) {
      return collection(response, this.item, function (response) {
        return {
          type : 'project'
        };
      });
    }
  };

  /* crm */
  factories.crm = {
    item : function (response) {
      return {
        id : response.Id
      };
    }
  };

  var create = function (apiurl, response, responses) {
    var apiAnchorsInd = apiAnchors.length;
    while (apiAnchorsInd--) {
      if (apiAnchors[apiAnchorsInd].re.test(apiurl)) {
        break;
      }
    }

    if (apiAnchorsInd !== -1) {
      var
        handlername = apiAnchors[apiAnchorsInd].handler,
        namespaces = handlername.split('-');

      if (namespaces.length > 1) {
        if (factories.hasOwnProperty(namespaces[0]) && typeof factories[namespaces[0]][namespaces[1]] === 'function') {
          return factories[namespaces[0]][namespaces[1]].apply(factories[namespaces[0]], [response, responses]);
        }
      }

      if (typeof factories[handlername] === 'function') {
        return factories[handlername].apply(factories, [response, responses]);
      }
    }

    return null;
  };

  return {
    init  : init,

    create : create
  };
})()
